﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using Clandestine;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Xml;

namespace Slick.MapEditor
{
    public delegate void TilesetsChangeEvent();

    public static class Tilesets
    {
        // Event stuffs to handle tilesets changing!
        public static event TilesetsChangeEvent OnTilesetsChange;

        // List of all tilesets loaded!
        public static List<string> LoadedTilesets = new List<string>();

        // Tile seletction. Probably a good reason these are separate, but I don't know why.
        private static Form lastTileSelector = null;
        private static List<List<string>> selectedTiles = new List<List<string>>();
        public static List<List<string>> SelectedTiles
        {
            get { return selectedTiles; }
        }
        
        // Saves currently loaded tilesheets to the XML file
        public static Dictionary<string, int> Save(XmlTextWriter xml)
        {
            // Save each tilesheet
            xml.WriteStartElement("spritesheets");
            Dictionary<string, int> tilesetToIndexMapping = new Dictionary<string, int>();
            lock (LoadedTilesets)
            {
                for (int i = 0; i < LoadedTilesets.Count; i++)
                {
                    tilesetToIndexMapping.Add(LoadedTilesets[i], i);
                    xml.WriteStartElement("spritesheet");
                    xml.WriteAttributeString("name", LoadedTilesets[i]);
                    xml.WriteEndElement();
                }
            }
            xml.WriteEndElement(); // </spritesheets>
            return tilesetToIndexMapping;
        }

        // Selects tiles!
        public static void SelectTiles(List<List<string>> tilenames)
        {
            // I dont know what this does!
            lock (selectedTiles)
            {
                if (tilenames.Count == 0)
                    return;

                selectedTiles = tilenames;
            }
        }

        // Loads a tileset
        public static void LoadTileset(string filename)
        {
            string name = filename.ToLower();

            lock (LoadedTilesets)
            {
                if (LoadedTilesets.Contains(name))
                    return;


                // Ah. Cutting time!

                // fix for oddly sized spritesheets (all of them w/variable grid size)
                if (filename.ToLower() != "special tiles")
                {
                    Bitmap temp = new Bitmap(filename);
                    Texture.SetTexture(filename, new Texture(new Bitmap(temp,
                        temp.Width - (temp.Width % Program.GridSize),
                        temp.Height - (temp.Height % Program.GridSize))), false);
                }
                else
                {
                    Texture.SetTexture("Special Tiles", new Texture("specialtiles.png"), false); // ugh!
                    MessageBox.Show("'Special Tiles' has been requested as a tileset."
                        + "\nThis is deprecated! Give it up!!");
                }

                // Back to normal(!)
                Texture t = Texture.GetTexture(filename);
                Bitmap bmpTileset = t.Bitmap;

                if (bmpTileset.Width % Program.GridSize != 0 || bmpTileset.Height % Program.GridSize != 0)
                    Log.w(filename + ": The tileset's size is not divisible by Program.GridSize, in spite of the fact we use (sized) tiles!");

                Rectangle tileDstRect = new Rectangle(0, 0, Program.GridSize, Program.GridSize);

                LoadedTilesets.Add(name);
            }

            if (OnTilesetsChange != null)
                OnTilesetsChange();
        }

        // Cuts a single tile from the spritesheet and makes a texture for it!
        // Doesn't return anything as the tile can be gotten by some weird texture magic like "tileSheetName!x!y" or some such
        internal static void CutSingleTile(string tilesetName, int n)
        {
            Texture t = Texture.GetTexture(tilesetName);
            Bitmap bmpTileset = t.Bitmap;

            int y = ((Program.GridSize * n) / bmpTileset.Width) * Program.GridSize; // int div!!
            int x = (n * Program.GridSize) % bmpTileset.Width;

            string tileName = tilesetName + "!" + n.ToString();

            // This makes reloading the tilesets useless.
            Bitmap bmpTile = new Bitmap(Program.GridSize, Program.GridSize);

            lock (t.bitmap)
            {
                // Cut out tile, and store in bmpTile
                BitmapData bs = bmpTileset.LockBits(new Rectangle(x, y, Program.GridSize, Program.GridSize),
                ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
                BitmapData bt = bmpTile.LockBits(new Rectangle(0, 0, Program.GridSize, Program.GridSize),
                    ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);

                unsafe
                {
                    byte* srcPtr = ((byte*)bs.Scan0.ToPointer());
                    byte* dstPtr = ((byte*)bt.Scan0.ToPointer());

                    for (int yy = 0; yy < Program.GridSize; yy++)
                    {
                        for (int bb = 0; bb < Program.GridSize * 4; bb++)
                            *(dstPtr + bb) = *(srcPtr + bb);

                        srcPtr += bs.Stride;
                        dstPtr += bt.Stride;
                    }
                }

                bmpTile.UnlockBits(bt);
                bmpTileset.UnlockBits(bs);
            }

            // Create a new Texture of bmpTile, and immediately change it's name
            // to tileset!tilenumber. (e.g. zeldatile1.png!22)
            // Indexed-bitmap tilesets have zelda pink as their translucency.
            // RGB bitmaps will of course have the (0,0) to deal with most likely
            // ...and RGBA is left as-is. :)
            Texture.SetTexture(tileName, new Texture(bmpTile,
                (bmpTileset.PixelFormat & System.Drawing.Imaging.PixelFormat.Indexed)
                == System.Drawing.Imaging.PixelFormat.Indexed), false);
            //}
        }
    }
}
